package org.linlinjava.litemall.wx.service;

import com.github.binarywang.wxpay.bean.notify.WxPayNotifyResponse;
import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
import com.github.binarywang.wxpay.bean.order.WxPayNativeOrderResult;
import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
import com.github.binarywang.wxpay.bean.result.BaseWxPayResult;
import com.github.binarywang.wxpay.config.WxPayConfig;
import com.github.binarywang.wxpay.constant.WxPayConstants;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.util.SignUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.linlinjava.litemall.core.notify.NotifyService;
import org.linlinjava.litemall.core.task.TaskService;
import org.linlinjava.litemall.core.util.IpUtil;
import org.linlinjava.litemall.core.util.JacksonUtil;
import org.linlinjava.litemall.core.util.ResponseUtil;
import org.linlinjava.litemall.db.domain.LitemallDisputeOrder;
import org.linlinjava.litemall.db.domain.LitemallOrder;
import org.linlinjava.litemall.db.domain.NewsPay;
import org.linlinjava.litemall.db.service.LitemallDisputeService;
import org.linlinjava.litemall.db.service.LitemallOrderService;
import org.linlinjava.litemall.db.service.NewsPayService;
import org.linlinjava.litemall.db.util.DisputeConstant;
import org.linlinjava.litemall.db.util.OrderConstant;
import org.linlinjava.litemall.wx.util.WXPayUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Map;


/**
 * @description:
 * @program: mall
 * @author: youvy
 * @create: 2019-11-23 20:46
 **/
@Service
public class PayService {
	private final Log logger = LogFactory.getLog(PayService.class);
	@Autowired
	private LitemallOrderService orderService;
	@Autowired
	private com.github.binarywang.wxpay.service.WxPayService wxPayService;
	@Autowired
	private NotifyService notifyService;
	@Autowired
	private TaskService taskService;
	@Autowired
	private NewsPayService newsPayService;
	@Autowired
	private LitemallDisputeService disputeService;
	@Autowired
	private WxPayConfig wxPayConfig;


	/**
	 * 微信扫码支付
	 *
	 * @param userId  用户ID
	 * @param body    订单编号 orderId
	 * @param request
	 * @return
	 */

	@Transactional
	public Object nativePay(Integer userId, String body, HttpServletRequest request) {
		if (userId == null) { return ResponseUtil.unlogin();}
		Integer orderId = JacksonUtil.parseInteger(body, "orderId");
		if (orderId == null) { return ResponseUtil.badArgument(); }

		LitemallOrder order = orderService.findById(orderId);
		if (order == null) { return ResponseUtil.badArgumentValue(); }

		if (!order.getUserId().equals(userId)) { return ResponseUtil.badArgumentValue(); }
		//本地订单结果
		WxPayNativeOrderResult result ;
		try {
			//微信支付:统一订单
			WxPayUnifiedOrderRequest orderRequest = new WxPayUnifiedOrderRequest();
			orderRequest.setOutTradeNo(order.getOrderSn());
			orderRequest.setTradeType("NATIVE");
			orderRequest.setBody("订单：" + order.getOrderSn());
			orderRequest.setProductId(order.getOrderSn());
			// 圆转成分   (圆角分:不是角)
			int fee ;
			BigDecimal actualPrice = order.getActualPrice();
			if (order.getGoodsPrice()!=null){

				fee = actualPrice.add(order.getGoodsPrice()).multiply(new BigDecimal(100)).intValue();
			}else {
				fee = actualPrice.multiply(new BigDecimal(100)).intValue();
			}

			orderRequest.setTotalFee(fee);
			orderRequest.setSpbillCreateIp(IpUtil.getIpAddr(request));

			result = wxPayService.createOrder(orderRequest);

			order.setPayCreateTime(LocalDateTime.now());
			orderService.updateWithOptimisticLocker(order);
		} catch (Exception e) {
			System.out.println(e);
			e.printStackTrace();
			return ResponseUtil.fail(801, "微信支付订单生成错误");
		}
		return result.getCodeUrl();
	}

	/**
	 * 微信付款成功或失败回调接口
	 * <p>
	 * 1. 检测当前订单是否是付款状态;
	 * 2. 设置订单付款成功状态相关信息;
	 * 3. 响应微信商户平台.
	 *
	 * @param request  请求内容
	 * @param response 响应内容
	 * @return 操作结果
	 */
	@Transactional
	public Object payNotify(HttpServletRequest request, HttpServletResponse response) throws Exception {
		String xmlResult = null;
		try {
			xmlResult = IOUtils.toString(request.getInputStream(), request.getCharacterEncoding());
			logger.info(xmlResult);
		} catch (IOException e) {
			e.printStackTrace();
			return WxPayNotifyResponse.fail(e.getMessage());
		}

		WxPayOrderNotifyResult result = null;
		try {
			result = wxPayService.parseOrderNotifyResult(xmlResult);

			if (!WxPayConstants.ResultCode.SUCCESS.equals(result.getResultCode())) {
				logger.error(xmlResult);
				throw new WxPayException("微信通知支付失败！");
			}
			if (!WxPayConstants.ResultCode.SUCCESS.equals(result.getReturnCode())) {
				logger.error(xmlResult);
				throw new WxPayException("微信通知支付失败！");
			}
		} catch (WxPayException e) {
			//e.printStackTrace();
			return WxPayNotifyResponse.fail(e.getMessage());
		}

		logger.info("处理腾讯支付平台的订单支付");
		logger.info(result);


		//校验签名，从wxConfig中获取参数
		String signType = wxPayConfig.getSignType();
		String mchKey = wxPayConfig.getMchKey();

		//调用包中的工具类
		Map<String, String> map = WXPayUtil.xmlToMap(xmlResult);
		boolean checkSign = SignUtils.checkSign(map, signType, mchKey);

		if (!checkSign){
			return WxPayNotifyResponse.fail("签名校验失败");
		}

		String orderSn = result.getOutTradeNo();
		String payId = result.getTransactionId();

		if (orderSn.contains("news")) {
			//新闻
			int id = Integer.parseInt(orderSn.split("_")[1]);
			int userId = Integer.parseInt(orderSn.split("_")[2]);
			NewsPay newsPay = newsPayService.findByNewsId(id, userId);
			if (newsPay == null) {
				return WxPayNotifyResponse.fail("订单不存在 sn=" + orderSn);
			}

			// 检查这个订单是否已经处理过
			if (newsPay.getPayStatus() == 1) {
				return WxPayNotifyResponse.success("订单已经处理成功!");
			}

			newsPay.setPayStatus(1);
			newsPayService.update(newsPay);

			return WxPayNotifyResponse.success("处理成功!");


		} else if (orderSn.contains("margin")) {
			//保证金
			// 分转化成元
			String totalFee = BaseWxPayResult.fenToYuan(result.getTotalFee());
			String sn = orderSn.split("_")[1];
			LitemallDisputeOrder order = disputeService.findOrderBySn(sn);
			if (order == null) {
				return WxPayNotifyResponse.fail("订单不存在 sn=" + orderSn);
			}

			// 检查这个订单是否已经处理过
			if (order.getState().shortValue() == DisputeConstant.RECHECKING) {
				return WxPayNotifyResponse.success("订单已经处理成功!");
			}

			// 检查支付订单金额
			if (!totalFee.equals(order.getPayAmount().toString())) {
				return WxPayNotifyResponse.fail(order.getDisputeSn() + " : 支付金额不符合 totalFee=" + totalFee);
			}
			order.setPayId(payId);
			order.setPayTime(LocalDateTime.now());
			order.setState(DisputeConstant.RECHECKING);
			disputeService.updateWithOptimisticLocker(order);
			return WxPayNotifyResponse.success("处理成功!");

		} else {
			//商品订单

			// 分转化成元
			String totalFee = BaseWxPayResult.fenToYuan(result.getTotalFee());
			LitemallOrder order = orderService.findBySn(orderSn);
			if (order == null) {
				return WxPayNotifyResponse.fail("订单不存在 sn=" + orderSn);
			}

			// 检查这个订单是否已经处理过
			if (order.getOrderStatus().shortValue() == OrderConstant.PAYED) {
				return WxPayNotifyResponse.success("订单已经处理成功!");
			}

			// 检查支付订单金额
/*			if (!totalFee.equals(order.getActualPrice().toString())) {
				return WxPayNotifyResponse.fail(order.getOrderSn() + " : 支付金额不符合 totalFee=" + totalFee);
			}*/
			//GoodsPrice().是保险的价格
			BigDecimal actualPrice = order.getActualPrice();
			BigDecimal goodsPrice = order.getGoodsPrice();
			if (!totalFee.equals(actualPrice.add(goodsPrice).toString())) {
				return WxPayNotifyResponse.fail(order.getOrderSn() + " : 支付金额不符合 totalFee=" + totalFee);
			}

			order.setPayId(payId);
			order.setPayTime(LocalDateTime.now());
			order.setOrderStatus(OrderConstant.PAYED);
			order.setPromiseTime(LocalDateTime.now().plusDays(order.getPromiseDate()));
			orderService.updateWithOptimisticLocker(order);
/*		if (orderService.updateWithOptimisticLocker(order) == 0) {
			// 这里可能存在这样一个问题，用户支付和系统自动取消订单发生在同时
			// 如果数据库首先因为系统自动取消订单而更新了订单状态；
			// 此时用户支付完成回调这里也要更新数据库，而由于乐观锁机制这里的更新会失败
			// 因此，这里会重新读取数据库检查状态是否是订单自动取消，如果是则更新成支付状态。
			order = orderService.findBySn(orderSn);
			int updated = 0;
			if (OrderUtil.isAutoCancelStatus(order)) {
				order.setPayId(payId);
				order.setPayTime(LocalDateTime.now());
				order.setOrderStatus(OrderUtil.STATUS_PAY);
				updated = orderService.updateWithOptimisticLocker(order);
			}

			// 如果updated是0，那么数据库更新失败
			if (updated == 0) {
				return WxPayNotifyResponse.fail("更新数据已失效");
			}
		}*/
/*		//TODO 发送邮件和短信通知，这里采用异步发送
		// 订单支付成功以后，会发送短信给用户，以及发送邮件给管理员
		notifyService.notifyMail("新订单通知", order.toString());
		// 这里微信的短信平台对参数长度有限制，所以将订单号只截取后6位
		notifyService.notifySmsTemplateSync(order.getMobile(), NotifyType.PAY_SUCCEED, new String[]{orderSn.substring(8, 14)});

		// 请依据自己的模版消息配置更改参数
		String[] parms = new String[]{
				order.getOrderSn(),
				order.getOrderPrice().toString(),
				DateTimeUtil.getDateTimeDisplayString(order.getAddTime()),
				order.getConsignee(),
				order.getMobile(),
				order.getAddress()
		};

		notifyService.notifyWxTemplate(result.getOpenid(), NotifyType.PAY_SUCCEED, parms, "pages/index/index?orderId=" + order.getId());*/
			// 取消订单超时未支付任务
			//taskService.removeTask(new OrderUnpaidTask(order.getId()));
			return WxPayNotifyResponse.success("处理成功!");
		}
	}

	@Transactional
	public Object nativePayNews(Integer userId, String body, HttpServletRequest request) {
		if (userId == null) {
			return ResponseUtil.unlogin();
		}
		Integer id = JacksonUtil.parseInteger(body, "id");
		if (id == null) {
			return ResponseUtil.badArgument();
		}
		WxPayNativeOrderResult result = null;
		try {
			WxPayUnifiedOrderRequest orderRequest = new WxPayUnifiedOrderRequest();
			orderRequest.setOutTradeNo("news_" + id + "_" + userId);
			orderRequest.setTradeType("NATIVE");
			orderRequest.setBody("订单：" + "News" + id + "_" + userId);
			orderRequest.setProductId("News" + id + "_" + userId);
			// 元转成分
			int fee = 0;
			BigDecimal actualPrice = new BigDecimal(0.01);
			fee = actualPrice.multiply(new BigDecimal(100)).intValue();
			orderRequest.setTotalFee(100);
			orderRequest.setSpbillCreateIp(IpUtil.getIpAddr(request));

			result = wxPayService.createOrder(orderRequest);

			int i = newsPayService.countByNewsIdAndUserId(id, userId);
			if (i == 0) {
				NewsPay newsPay = new NewsPay(userId, id, 0);
				newsPayService.insert(newsPay);
			}

		} catch (Exception e) {
			System.out.println(e);
			e.printStackTrace();
			return ResponseUtil.fail(801, "微信支付订单生成错误");
		}
		return result.getCodeUrl();
	}

	public Object nativePayMargin(Integer userId, String body, HttpServletRequest request) {
		if (userId == null) {
			return ResponseUtil.unlogin();
		}

		Integer disputeId = JacksonUtil.parseInteger(body, "disputeId");
		LitemallDisputeOrder disputeOrder = disputeService.findOrderById(disputeId);
		String tradeNo = disputeOrder.getDisputeSn();
		WxPayNativeOrderResult result = null;
		try {
			WxPayUnifiedOrderRequest orderRequest = new WxPayUnifiedOrderRequest();
			orderRequest.setOutTradeNo("margin_" + tradeNo);
			orderRequest.setTradeType("NATIVE");
			orderRequest.setBody("订单：" + "Margin" + "_" + tradeNo);
			orderRequest.setProductId("Margin"  + "_" + tradeNo);
			// 元转成分
			int fee = 0;
			BigDecimal actualPrice = disputeOrder.getPayAmount();
			fee = actualPrice.multiply(new BigDecimal(100)).intValue();
			orderRequest.setTotalFee(fee);
			orderRequest.setSpbillCreateIp(IpUtil.getIpAddr(request));

			result = wxPayService.createOrder(orderRequest);


		} catch (Exception e) {
			System.out.println(e);
			e.printStackTrace();
			return ResponseUtil.fail(801, "微信支付订单生成错误");
		}
		return result.getCodeUrl();
	}
}
